/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package multiface.web.util.categories;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.*;
import multiface.valentinorusso.jpa.Categoria;
import multiface.valentinorusso.jpa.util.PersistenceManager;

/**
 *
 * @author Diego64
 */
public class AlberoCategorie implements AlberoCategorieInterface {

    private AlberoCategorieNode root;
    private int IDROOT = -1;

    public AlberoCategorie() {
        this.root = new AlberoCategorieNode();
        this.root.setLevel(IDROOT);
    }

    @Override
    public void caricaDaDB() {
        EntityManagerFactory emf = PersistenceManager.getInstance().getEntityManagerFactory();
        EntityManager em = emf.createEntityManager();

        List<Categoria> list = em.createNativeQuery("SELECT * FROM categoria c ORDER BY c.sopracategoria, c.ordine", Categoria.class).getResultList();

        Iterator i = list.iterator();
        while (i.hasNext()) {
            Categoria c = (Categoria) i.next();
            em.refresh(c);
            this.addCategoria(c);
        }
        em.close();
        //emf.close();
    }

    @Override
    public List<Categoria> categorieDaLivello(int level) throws Exception {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public List<Categoria> categorieInOrdineDiProfondita() {
        LinkedList<AlberoCategorieNode> lnode = this.toList();
        LinkedList<Categoria> lcat = new LinkedList<Categoria>();

        for (int i = 1; i < lnode.size(); i++) {
            lcat.add(i - 1, lnode.get(i).getCategoria());
        }
        return lcat;
    }

    @Override
    public int altezza() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public List<Categoria> getFigli(Categoria padre) {
        return this.ConvertToCategories(find(padre.getIdcategoria()).getFigli());
    }

    @Override
    public List<Categoria> getFratelli(Categoria cat) {
        return this.ConvertToCategories(find(cat.getSopracategoria()).getFigli());
    }

    @Override
    public int numeroFigli(Categoria padre) throws Exception {
        return this.getFigli(padre).size();
    }

    @Override
    public Categoria cercaCategoria(int Id) {
        return this.find(Id).getCategoria();
    }
    
    public void eliminaCategoria(int idCat){
        EntityManagerFactory emf = PersistenceManager.getInstance().getEntityManagerFactory();
        EntityManager em = emf.createEntityManager();
        
        AlberoCategorieNode acn_in = find(idCat);
        if (acn_in == null) {
            System.out.println("ERROR: Nodo cat_in non presente");
            return;
        }
        Categoria cat_in = acn_in.getCategoria();
        List<Categoria> fratelli_cat_in = getFratelli(cat_in);
        for (Categoria s : fratelli_cat_in) {
            if (s.getOrdine() > cat_in.getOrdine()) {
                em.getTransaction().begin();
                s.setOrdine(s.getOrdine() - 1);
                em.merge(s);
                em.getTransaction().commit();
            }
        }
        
        em.getTransaction().begin();
        Categoria c = em.find(Categoria.class,idCat);
        em.remove(c);
        em.getTransaction().commit();
        
        em.close();
    }

    @Override
    public void spostaCategoriaInPosizione(int cat_in_int, int cat_to_int, int ordine) {
        EntityManagerFactory emf = PersistenceManager.getInstance().getEntityManagerFactory();
        EntityManager em = emf.createEntityManager();

        //Controllo se in nodi esistono nell'albero. Dovrebbero essere SEMPRE presenti.
        AlberoCategorieNode acn_in = find(cat_in_int);
        if (acn_in == null) {
            System.out.println("ERROR: Nodo cat_in non presente");
            return;
        }
        AlberoCategorieNode acn_to = find(cat_to_int);
        if (acn_to == null) {
            System.out.println("ERROR: Nodo cat_to non presente");
            return;
        }

        Categoria cat_in = acn_in.getCategoria();
        Categoria cat_to = acn_to.getCategoria();

        List<Categoria> fratelli_cat_in = getFratelli(cat_in);
        List<Categoria> figli_cat_to = getFigli(cat_to);

        // Sistemo i fratelli di cat_in
        for (Categoria s : fratelli_cat_in) {
            if (s.getOrdine() > cat_in.getOrdine()) {
                em.getTransaction().begin();
                s.setOrdine(s.getOrdine() - 1);
                em.merge(s);
                em.getTransaction().commit();
            }
        }
        
        // Blocco della modifica di cat_in
        em.getTransaction().begin();
        //Setto il nuovo padre ad cat_in che sarebbe cat_to
        cat_in.setSopracategoria(cat_to_int);
        //Setto il nuovo ordine
        cat_in.setOrdine(ordine);
        em.merge(cat_in);
        em.getTransaction().commit();


        //sistemo i figli di cat_to
        for (Categoria s : figli_cat_to) {
            if (s.getOrdine() >= ordine && s.getIdcategoria() != cat_in.getIdcategoria()) {
                em.getTransaction().begin();
                s.setOrdine((s.getOrdine() + 1));
                em.merge(s);
                em.getTransaction().commit();
              }
        }

        em.close();
    }
    
    @Override
    public void spostaCategoria(int cat_in_int, int cat_to_int) {
        spostaCategoriaInPosizione(cat_in_int, cat_to_int, 0);
    }

    @Override
    public String toString() {
        toStringAUX(root);
        return "";
    }
    private void toStringAUX(AlberoCategorieNode n) {
        if (n == null || n.getCategoria() == null) {
            return;
        }

        ////Sy_stem.out.println(n.getCategoria());
        List<AlberoCategorieNode> figli = n.getFigli();
        Iterator it = figli.iterator();
        while (it.hasNext()) {
            AlberoCategorieNode node = (AlberoCategorieNode) it.next();
            toStringAUX(node);
        }
    }

    @Override
    public void addCategoria(Categoria c) {

        AlberoCategorieNode padre = find(c.getSopracategoria());
        if (padre != null) {
            ////Sy_stem.out.println("Add category " +c.getNome() + " in to Padre : " + padre.getCategoria().getNome());
            AlberoCategorieNode n = new AlberoCategorieNode(c);

            // Aggiungo il collegamenteo al padre
            n.setPadre(padre);
            padre.getFigli().add(n);
        } else {
            //Sy_stem.out.println("Null Finder");
        }
    }

    public List<Categoria> GetSottoCategorie(Categoria c) {
        List<Categoria> list = new LinkedList<Categoria>();


        List<AlberoCategorieNode> ln = new LinkedList<AlberoCategorieNode>();
        sub(find(c.getIdcategoria()), ln);

        list.addAll(ConvertToCategories(ln));

        return list;
    }

    private void sub(AlberoCategorieNode n, List<AlberoCategorieNode> list) {
        if (n == null || n.getCategoria() == null) {
            return;
        }
        list.add(n);

        List<AlberoCategorieNode> figli = n.getFigli();
        Iterator it = figli.iterator();
        while (it.hasNext()) {
            AlberoCategorieNode node = (AlberoCategorieNode) it.next();
            sub(node, list);
        }
    }

    public String Percorso(Categoria c) {
        AlberoCategorieNode n = find(c.getIdcategoria());

        LinkedList<String> l = new LinkedList<String>();

        AlberoCategorieNode padre = n;
        while (padre != null) {
            if (padre.getCategoria().getIdcategoria() == IDROOT) {
                padre = null;
            } else {
                l.addFirst((padre.getCategoria().getOrdine() + 1) + "");
                padre = padre.getPadre();
            }
        }

        String s = "";
        for (int i = 0; i < l.size(); i++) {
            s += l.get(i);
            //il punino lo lasciato perche mi serve dopo per aggiungere l'ordine da selezionare
            s += ".";
        }
        return s;
    }

    public String PercorsoName(Categoria c) {
        AlberoCategorieNode n = find(c.getIdcategoria());

        LinkedList<String> l = new LinkedList<String>();

        AlberoCategorieNode padre = n;
        while (padre != null) {
            if (padre.getCategoria().getIdcategoria() == IDROOT) {
                padre = null;
            } else {
                l.addFirst(padre.getCategoria().getNome());
                padre = padre.getPadre();
            }
        }

        String s = "";
        for (int i = 0; i < l.size(); i++) {
            s += l.get(i);
            if (i < l.size() - 1) {
                s += " > ";
            }
        }
        return s;
    }

    public int Altezza(Categoria c) {
        AlberoCategorieNode n = find(c.getIdcategoria());
        int i = 0;

        AlberoCategorieNode padre = n;
        while (padre != null) {
            if (padre.getCategoria().getIdcategoria() == IDROOT) {
                padre = null;
            } else {
                i++;
                padre = padre.getPadre();
            }
        }
        return i;
    }

    private List<Categoria> ConvertToCategories(List<AlberoCategorieNode> acn) {
        LinkedList<Categoria> lc = new LinkedList<Categoria>();
        for (AlberoCategorieNode n : acn) {
            lc.addLast(n.getCategoria());
        }
        return lc;
    }

    public AlberoCategorieNode find(int idcategoria) {
        if (idcategoria == IDROOT) {
            return root;
        }

        LinkedList<AlberoCategorieNode> l = new LinkedList<AlberoCategorieNode>();
        AlberoCategorieNode n = root;
        findAUX(n, idcategoria, l);


        if (l.isEmpty()) {
            return null;
        } else {
            return l.getFirst();
        }

    }

    private void findAUX(AlberoCategorieNode n, int idcategoria, List<AlberoCategorieNode> list) {


        if (n == null || n.getCategoria() == null) {
            return;
        }

        if (n.getCategoria().getIdcategoria() == idcategoria) {
            list.add(n);
            return;
        }

        List<AlberoCategorieNode> figli = n.getFigli();
        Iterator it = figli.iterator();
        while (it.hasNext()) {
            AlberoCategorieNode node = (AlberoCategorieNode) it.next();
            findAUX(node, idcategoria, list);
        }
    }

    public LinkedList<AlberoCategorieNode> toList() {
        LinkedList<AlberoCategorieNode> list = new LinkedList<AlberoCategorieNode>();
        toListAUX(root, list);
        return list;
    }

    private void toListAUX(AlberoCategorieNode n, LinkedList<AlberoCategorieNode> l) {
        if (n == null || n.getCategoria() == null) {
            return;
        }

        l.addLast(n);

        List<AlberoCategorieNode> figli = n.getFigli();
        Iterator it = figli.iterator();
        while (it.hasNext()) {
            AlberoCategorieNode node = (AlberoCategorieNode) it.next();
            toListAUX(node, l);
        }
    }

    //rev 459
    public LinkedList<AlberoCategorieNode> AlberoGenealogico(Categoria c) {
        AlberoCategorieNode n = find(c.getIdcategoria());

        LinkedList<AlberoCategorieNode> l = new LinkedList<AlberoCategorieNode>();

        AlberoCategorieNode padre = n;
        while (padre != null) {
            if (padre.getCategoria().getIdcategoria() == IDROOT) {
                padre = null;
            } else {
                l.addFirst(padre);
                padre = padre.getPadre();
            }
        }

        return l;
    }

    public boolean Discendenza(int catA, int catB) {
        List<AlberoCategorieNode> ag = this.AlberoGenealogico(new Categoria(catB));

        for (AlberoCategorieNode ac : ag) {
            if (ac.getCategoria().getIdcategoria() == catA) {
                return true;
            }
        }

        List<AlberoCategorieNode> ag2 = this.AlberoGenealogico(new Categoria(catA));

        for (AlberoCategorieNode ac : ag2) {
            if (ac.getCategoria().getIdcategoria() == catB) {
                return true;
            }
        }

        return false;
    }

    public boolean Parentela(int catA, int catB) {
        List<AlberoCategorieNode> ag = this.AlberoGenealogico(new Categoria(catB));

        for (AlberoCategorieNode ac : ag) {
            if (ac.getCategoria().getIdcategoria() == catA) {
                return true;
            }
        }

        List<AlberoCategorieNode> ag2 = this.AlberoGenealogico(new Categoria(catA));

        for (AlberoCategorieNode ac : ag2) {
            if (ac.getCategoria().getIdcategoria() == catB) {
                return true;
            }
        }

        for (AlberoCategorieNode ac : find(catA).getPadre().getFigli()) {
            if (ac.getCategoria().getIdcategoria() == catB) {
                return true;
            }
        }
        return false;
    }
    
    public int getMaxOrdineFratelli(int cat){
        List<Categoria> l = this.getFratelli(find(cat).getCategoria());
        int max = -1;
        for(Categoria c : l){
            if(c.getOrdine()>max){max=c.getOrdine();}
        }
        return max+1;
    }
    
    public int getMaxOrdineFigli(int cat){
        List<Categoria> l = this.getFigli(find(cat).getCategoria());
        int max = -1;
        for(Categoria c : l){
            if(c.getOrdine()>max){max=c.getOrdine();}
        }
        return max+1;
    }
}
